#include "render_system/render_engine.h"

#include <exception>

#include "render_system/gl_header.h"
#include "utility.h"


RenderEngine RenderEngine::_singleton;


RenderEngine& RenderEngine::ref()
{
    return _singleton;
}


void RenderEngine::init(const std::string& windowName)
{
    try
    {
        int argc = 0;
        char** argv = NULL;
        glutInit(&argc, argv);

        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
        glutInitWindowSize(800, 800);

        glutCreateWindow(windowName.c_str());

        if (GLEW_OK != glewInit())
        {
            throw std::exception("GLEW initialization failed.");
        }

        glShadeModel(GL_SMOOTH);
        glClearDepth(1.0f);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);
        glFrontFace(GL_CCW);        // Counter-clockwise polygons face out

        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

        glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    }
    catch(const std::exception& e)
    {
        Log::error(e.what());
        throw;
    }
}


void RenderEngine::setScene( const ScenePtr pScene )
{
    try
    {
        if (nullptr == pScene)
        {
            throw std::exception("scene pointer is null.");
        }
        _pScene = pScene;
    }
    catch(const std::exception& e)
    {
        Log::error(e.what());
        throw;
    }
}


void RenderEngine::run()
{
    if (NULL == _pScene)
    {
        Log::error("no scene to render.");
        return;
    }
    else
    {
        _registerEvent();
        glutMainLoop();
    }
}


void RenderEngine::_registerEvent()
{
    //glutSpecialFunc(onSpecialKeyDown);
    glutDisplayFunc(&RenderEngine::_onRenderWrapper);
    glutReshapeFunc(&RenderEngine::_onSizeChangedWrapper);

    glutMouseFunc(&RenderEngine::_onMouseButtonEventWrapper);
    glutMotionFunc(&RenderEngine::_onMouseMoveWrapper);

    glutKeyboardFunc(&RenderEngine::_onKeyboardWrapper);
    glutSpecialFunc(&RenderEngine::_onSpecialKeyPressedWrapper);
}


void RenderEngine::_onRenderWrapper()
{
    _singleton._pScene->onRender();
    glutSwapBuffers();
}


void RenderEngine::_onSizeChangedWrapper( int width, int height )
{
    _singleton._pScene->onSizeChanged(width, height);
    _singleton._pScene->onRender();
    glutSwapBuffers();
}

void RenderEngine::_onMouseMoveWrapper( int x, int y )
{
    _singleton._pScene->onMouseMove(x, y);
}

void RenderEngine::_onKeyboardWrapper( unsigned char key, int x, int y )
{
    _singleton._pScene->onKeyboard(key, x, y);
}

void RenderEngine::_onMouseButtonEventWrapper( int button,int state,int x,int y )
{
    _singleton._pScene->onMouseButtonEvent(button, state, x, y);
}

void RenderEngine::_onSpecialKeyPressedWrapper(int key, int x, int y)
{
    _singleton._pScene->onSpecialKeyPressed(key, x, y);
}